package httplogger

import (
	"bufio"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/goccy/go-json"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/pkg/errors"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"gitlab.local/DO-module/new-filemanage-module/src/constant"
	"gitlab.local/DO-module/new-filemanage-module/src/core/sqlitedb/sqlitemodel"
	"gitlab.local/golibrary/utils"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

const (
	linkZlog                = "/etc/tos/scripts/zloginit" // 连接zlog目录
	httpLog                 = "/var/log/zlog"             // httpLog目录
	tmpLog                  = "/tmp/httpLog/http_%s.log"  // httpLog 临时目录
	Access                  = "Login system successful!"
	LogOut                  = "Web logout successful!"
	PasswdErr               = "Password is error!"
	CodeErr                 = "Verification code error!"
	AddUser                 = "Create user %s Successful!"
	EditUser                = "Edit User %s Privileges!"
	DelUser                 = "Delete user %s Successful!"
	AddGroup                = "Create user group %s successfully!"
	EditGroup               = "Edit user group %s successfully!"
	DelGroup                = "Delete user group %s successfully!"
	AddFolder               = "Create shared folder %s successfully"
	DelFolder               = "Delete shared folder %s Successful!"
	AddIosFolder            = "Create ISO shared folder %s successfully"
	DelIosFolder            = "Delete ISO shared folder %s Successful!"
	EnabledSmb              = "SMB file service enabled successfully!"
	DisabledSmb             = "SMB file service disabled successfully!"
	EnabledAfp              = "AFP file service enabled successfully!"
	DisabledAfp             = "AFP file service disabled successfully!"
	EnabledFtp              = "FTP file service enabled successfully!"
	DisabledFtp             = "FTP file service disabled successfully!"
	EnabledSftp             = "SFTP file service enabled successfully!"
	DisabledSftp            = "SFTP file service disabled successfully!"
	EnabledNfs              = "NFS file service enabled successfully!"
	Disabled                = "NFS file service disabled successfully!"
	EnabledRsync            = "Rsync server enabled successfully!"
	DisabledRsync           = "Rsync server disabled successfully!"
	EnabledWebDav           = "WebDAV server enabled successfully!"
	DisabledWebDav          = "WebDAV server disabled successfully!"
	EnabledTelnet           = "Telnet enabled successfully!"
	DisabledTelnet          = "Telnet disabled successfully!"
	EnabledSSH              = "SSH enabled successfully!"
	DisabledSSH             = "SSH disabled successfully!"
	EnabledSnmp             = "SNMP enabled successfully!"
	DisabledSnmp            = "SNMP disabled successfully!"
	EnabledUpnp             = "UPnP discovery service enabled successfully!"
	DisabledUpnp            = "UPnP discovery service disabled successfully!"
	EnabledWindows          = "Windows discovery service enabled successfully!"
	DisabledWindows         = "Windows discovery service disabled successfully!"
	EnabledBonjour          = "Bonjour enabled successfully!"
	DisabledBonjour         = "Bonjour disabled successfully!"
	EnabledMedia            = "Media index enabled successfully!"
	DisabledMedia           = "Media index disabled successfully!"
	AddVolume               = "Volume %s created successfully!"
	AddVolumeErr            = "Volume %s creation failed!"
	EditVolume              = "Volume %s edited successfully!"
	DelVolume               = "Volume deleted successfully!"
	DelVolumeErr            = "Volume deletion failed!"
	AddStorage              = "Storage pool %s was created successfully!"
	AddStorageErr           = "Storage pool %s creation failed!"
	DelStorage              = "Storage pool %s was deleted successfully!"
	DelStorageErr           = "Storage pool %s deletion failed!"
	EnableSSD               = "SSD cache enabled successfully!"
	DisableSSD              = "SSD cache disabled successfully!"
	AddVirtualDisk          = "Virtual disk created successfully!"
	AddVirtualDiskErr       = "Virtual disk creation failed!"
	DelVirtualDisk          = "Virtual disk deleted successfully!"
	DelVirtualDiskErr       = "Virtual disk deletion failed!"
	FormattedVirtualDisk    = "The virtual disk LUN was formatted successfully!"
	FormattedVirtualDiskErr = "Teh Virtual disk LUN was formatted failed!"
	FormattedUSB            = "USB %s formatted successfully!"
	FormattedUSBErr         = "USB %s formatted failed!"
	Repair                  = "Repair the array!"
	Migrate                 = "Migrate the array!"
	AddDrives               = "Add hard drives to the array!"
	AddCapacity             = "Add capacity to the array!"
	AddTask                 = "Created scheduled task %s!"
	DelTask                 = "Removed scheduled task %s!"
	EnabledRemoteAccess     = "Enable TNAS.online remote access successfully!"
	EnabledRemoteAccessErr  = "Failed to enable TNAS.online remote access"
	DisabledRemoteAccess    = "Disable TNAS.online remote access successfully!"
	DisabledRemoteAccessErr = "Failed to disable TNAS.online remote access!"
	AddDDns                 = "DDNS connection %s created successfully!"
	DDnsLinkErr             = "DDNS connection %s creation failed!"
	DelDDns                 = "DDNS connection %s deleted successfully"
	DelDDnsErr              = "DDNS connection %s deleted failed"
)

var instance *Service
var once sync.Once

type myFormatter struct {
	logrus.TextFormatter
}

type HttpSysLog struct {
	Level    string `json:"Level"`    // 日志等级
	Time     string `json:"Time"`     // 写入时间
	Category string `json:"Category"` // 日志类别
	User     string `json:"User"`     // 操作用户
	Event    event  `json:"Event"`    // 操作事件
}

type event struct {
	Ip      string `json:"Ip"`      // 操作ip
	Port    string `json:"Port"`    // 请求port
	Message string `json:"message"` // 实际请求事件
}

type Service struct {
	logger    *logrus.Logger
	logWriter *rotatelogs.RotateLogs
	format    myFormatter
}

type Logger interface {
	Infoln(c *gin.Context, event string)
	Errorln(c *gin.Context, event string)
	Warnln(c *gin.Context, event string)
}

// Authorize /** 中间件
func Authorize() gin.HandlerFunc {
	return func(c *gin.Context) {
		New().SaveTo()
	}
}

func New() *Service {
	once.Do(func() {
		if !utils.Exists(httpLog) {
			_ = utils.RunShell(linkZlog)
		}
		instance = &Service{
			logger: logrus.New(),
			format: myFormatter{
				logrus.TextFormatter{
					TimestampFormat: "2006-01-02 15:04:05",
				},
			},
		}
		instance.logWriter, _ = rotatelogs.New(
			// 分割后的文件名称
			fmt.Sprintf(tmpLog, "%Y-%m-%d"),
			// 设置最大保存时间(30天)
			rotatelogs.WithMaxAge(30*24*time.Hour),
			// 设置日志切割时间间隔(1天)
			rotatelogs.WithRotationTime(24*time.Hour),
		)
		instance.logger.SetLevel(logrus.InfoLevel)
		// instance.logger.SetFormatter(&instance.format)
		instance.logger.SetOutput(io.Discard)
		// 加入 hook
		writeMap := lfshook.WriterMap{
			logrus.InfoLevel:  instance.logWriter,
			logrus.DebugLevel: instance.logWriter,
			logrus.WarnLevel:  instance.logWriter,
			logrus.ErrorLevel: instance.logWriter,
		}
		// instance.logWriter.Close()
		instance.logger.AddHook(lfshook.NewHook(writeMap, &instance.format))
	})
	return instance
}

func (f *myFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	fields := entry.Data
	sysLog := HttpSysLog{
		Level:    entry.Level.String(),
		Time:     entry.Time.Format(f.TimestampFormat),
		Category: fmt.Sprintf("%s", fields["Category"]),
		User:     fmt.Sprintf("%s", fields["User"]),
		Event: event{
			Ip:      fmt.Sprintf("%s", fields["Ip"]),
			Port:    fmt.Sprintf("%s", fields["Port"]),
			Message: entry.Message,
		},
	}
	sysLogByte, err := json.Marshal(sysLog)
	return []byte(string(sysLogByte) + "\n"), err
}

func (Self *Service) Infoln(c *gin.Context, event string) {
	if logger, ok := Self.setLogger(c); ok {
		logger.Infoln(event)
	}
}

func (Self *Service) Errorln(c *gin.Context, event string) {
	if logger, ok := Self.setLogger(c); ok {
		logger.Errorln(event)
	}
}

func (Self *Service) Warnln(c *gin.Context, event string) {
	if logger, ok := Self.setLogger(c); ok {
		logger.Warnln(event)
	}
}

func (Self *Service) setLogger(c *gin.Context) (logger *logrus.Entry, isTarget bool) {
	logDir := filepath.Dir(tmpLog)
	if !utils.Exists(logDir) {
		if err := os.MkdirAll(tmpLog, os.ModePerm); err != nil {
			return nil, false
		}
	}
	// 请求类型
	Category := "HTTP"
	// 请求用户
	userInfo, ok := c.Get(constant.ContextUserInfo)
	user := sqlitemodel.UserTable{}
	if ok {
		_ = json.Unmarshal(userInfo.([]byte), &user)
	}
	if len(user.Username) == 0 || user.Username == "" {
		// 登录时当前请求并没有记录登录信息至ContextUserInfo上下文中 尝试从ContextTryLoginUser获取当前用户
		if name, ok := c.Get(constant.ContextTryLoginUser); ok {
			user.Username = name.(string)
		} else {
			user.Username = "-"
		}
	}
	// 请求端口
	var port string // c.Request.URL.Port()
	addr := strings.Split(c.Request.RemoteAddr, ":")
	if len(addr) == 2 {
		port = addr[1]
	}
	filePath := Self.logWriter.CurrentFileName()
	if len(filePath) > 0 && !utils.Exists(filePath) {
		_ = Self.logWriter.Rotate()
	}
	return Self.logger.WithFields(logrus.Fields{
		"Category": Category,
		"User":     user.Username,
		"Ip":       c.ClientIP(),
		"Port":     port,
	}), true
}

func (Self *Service) SaveTo() { // 测试用转存
	if !utils.Exists(httpLog) {
		return
	}
	logDirPath := path.Dir(tmpLog)
	fs, err := os.ReadDir(logDirPath)
	if err != nil {
		return
	}
	for _, file := range fs {
		if strings.Index(file.Name(), "http") != 0 {
			continue
		}
		if !file.IsDir() {
			oldLogPath := path.Join(logDirPath, file.Name())
			newLogPath := path.Join(httpLog, file.Name())
			if err := Self.SetFileAppendData(oldLogPath, newLogPath); err != nil {
				continue
			}
		}
	}
}

// SetFileAppendData /** 文件追加内容
func (Self *Service) SetFileAppendData(tmpFile, path string) error {
	tmpF, err := os.OpenFile(tmpFile, os.O_RDWR, 0644)
	if err != nil {
		return errors.Wrap(err, "File open failed")
	}
	defer tmpF.Close()
	tmpStat, err := tmpF.Stat()
	if err != nil {
		return errors.Wrap(err, "Get file property failed")
	}
	if tmpStat.Size() == 0 {
		return nil
	}
	file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		return errors.Wrap(err, "File open failed")
	}
	defer file.Close()
	reader := bufio.NewReader(tmpF)
	for {
		b, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		if len(b) == 0 {
			continue
		}
		_, _ = file.WriteString(string(b) + "\n")
	}
	return tmpF.Truncate(0)
}
